/*
 * gscore.cc
 *
 *  Created on: Sep 25, 2012
 *      Author: shaohong
 */

#include "gscore.hh"
#include <numeric>

using namespace std;

static int round(double x)
{
   return int(x > 0.0 ? x + 0.5 : x - 0.5);
}

GolfScoreCard::GolfScoreCard(const GolfPlayer& player, const GolfCourse& course,
		TeeColor teeColor) :
		player(player), course(course), teeColor(teeColor) {
}

const GolfPlayer& GolfScoreCard::getPlayer() const {
	return player;
}

const GolfCourse& GolfScoreCard::getCourse() const {
	return this->course;
}

TeeColor GolfScoreCard::getTeeColor() const {
	return this->teeColor;
}

void GolfScoreCard::addResult(size_t result) {
	if (resultsPerHole.size() == 18) {
		return;
	}

	resultsPerHole.push_back(result);
}

const std::vector<size_t>& GolfScoreCard::getResultsPerHole() const {
	return this->resultsPerHole;

}

std::vector<size_t> GolfScoreCard::getPointsPerHole() const {
//	Course handicap = player handicap * (slope / 113) + (cr - course par)
	GolfPlayer player = getPlayer();

	// A map, which maps the gender and tee color to a pair of cr (course rating)
	// and slope. The cr and slope values effect the calculation of the course
	// handicap.

	pair<double, size_t> crSlopePair = getCourse().getDifficulty(
			player.getGender(), teeColor);
	int courseHandiCap = round (player.getHandicap()
			* (crSlopePair.second / 113.0)
			+ (crSlopePair.first - getCourse().getPar()));
//
//	cerr << "crSlopePair " << crSlopePair.first << "," << crSlopePair.second << endl;
//	cerr << "player.getHandicap() " << player.getHandicap() << endl;
//	cerr << "getCourse().getPar() " << getCourse().getPar() << endl;
//	cerr<< "courseHandiCap " << courseHandiCap << endl;

	//	player's own par per hole
	vector<size_t> playerPars;
	vector<GolfHole> holes = getCourse().getHoles();
	// initialize playerCalibratedPars
	for (size_t i = 0; i < holes.size(); i++) {
		playerPars.push_back(holes[i].getPar());
	}

	if (courseHandiCap > 0) {
		int addtionalStrokesPerHole = courseHandiCap / (18);
		for (size_t i = 0; i < playerPars.size(); i++) {
			playerPars[i] = playerPars[i] + addtionalStrokesPerHole;
		}

		size_t remainingAdditionalStrokes = courseHandiCap % (holes.size());
		for (size_t i = 1; i <= remainingAdditionalStrokes; i++) {
			// find the relevant hole, whose hcp equals i and add 1 to the corresponding player's par value
			for (size_t holeIdx = 0; holeIdx < holes.size(); holeIdx++) {
				if (holes[holeIdx].getHcp() == i) {
					playerPars[holeIdx] += 1;
					break;
				}
			}
		}
	}

	if (courseHandiCap < 0) {
		courseHandiCap = -1 * courseHandiCap;

		size_t addtionalStrokesPerHole = courseHandiCap / (18);
		for (size_t i = 0; i < playerPars.size(); i++) {
			playerPars[i] -= addtionalStrokesPerHole;
		}

		size_t remainingAdditionalStrokes = courseHandiCap % (holes.size());
		for (size_t i = 1; i <= remainingAdditionalStrokes; i++) {
			// find the relevant hole, whose hcp equals 18-i+1 and substract 1 from the corresponding player's par value
			for (size_t holeIdx = 0; holeIdx < holes.size(); holeIdx++) {
				if (holes[holeIdx].getHcp() == (18 - i + 1)) {
					playerPars[holeIdx] -= 1;
					break;
				}
			}
		}

	}

//    *  2. The resulting course handicap (an integer) signifies additional strokes
//    *     the player may take during the game. These strokes are then divided and
//    *     added to the pars of each hole for the Golf course.
//    *
//    *     The strokes are divided evenly, e.g. a course handicap of 18 gives each
//    *     hole one additional stroke to the par, 36 gives each hole two additional
//    *     strokes to the par.
//    *
//    *     When dividing less than 18 additional strokes, usually as the remainder
//    *     of a larger course handicap, the strokes are added in the order of hole
//    *     difficulty, starting from the most difficult hole, i.e. the hole with
//    *     hcp 1.
//    *
//    *     For negative course handicaps, the strokes are removed from the pars of
//    *     the holes in the order of decreasing difficulty, starting from the least
//    *     difficult hole, i.e. the hole with hcp 18.

//    * Now you should have a vector with the player's own par per hole. Next you
//    * should use this vector to calculate the points per hole.
//    *
//    * The points received for one hole is calculated as follows:
//    *
//    * 1. If the player meets his/her own par (calculated above), he/she receives
//    *    2 points for the hole.
//    *
//    * 2. Each each stroke over the player's own par reduces the score by one,
//    *    to a minimum of zero.
//    *
//    * 3. Each stroke less than the player's own par adds the score by one.
//    *
//    * 4. If the result for the hole is zero strokes, the score is automatically zero,
//    *    meaning that the player gave up during that hole.

	vector<size_t> pointsPerHole;
	for (size_t i = 0; i < resultsPerHole.size(); i++) {
		size_t actualStrokes = resultsPerHole[i];
		if (0 == actualStrokes) {
			pointsPerHole.push_back(0);
			continue;
		}

		int candidateScore = (playerPars[i] - actualStrokes) + 2;
		pointsPerHole.push_back(candidateScore >= 0 ? candidateScore : 0);
	}

	return pointsPerHole;
}

size_t GolfScoreCard::getTotalPoints() const {
	vector<size_t> pointsPerHole = getPointsPerHole();
	return accumulate(pointsPerHole.begin(), pointsPerHole.end(), 0);

}

string getTeeColor(TeeColor teeColor) {
	switch (teeColor) {
	case RED:
		return "red";

	case BLUE:
		return "blue";

	case YELLOW:
		return "yellow";

	case WHITE:
		return "white";
	}
}

std::ostream& operator<<(std::ostream& out, const GolfScoreCard& scoreCard) {
//	    * Player information:
//	    * Id: <player's id>
//	    * Name: <player's name>
//	    * Gender: <player's gender as a string, either male or female>
//	    * Handicap: <player's handicap>
//	    * Course: <course name>
//	    * Tee color: <tee color as a string either, red, blue, yellow or white>
//	    * Hole <id>: <hole par>/<hole hcp>/<player's hole result>/<player's points for the hole>

//	Player information:
//	Id: 0
//	Name: Petteri Koivumaki
//	Gender: male
//	Handicap: -7.5
//	Course: Sarfvik
//	Tee color: white
//	Hole 1: 5/10/7/0
//	Hole 2: 4/6/6/0
//	Hole 3: 3/18/3/1
//  ...
//	Hole 18: 3/11/5/0
//	Total points: 11
	out << scoreCard.getPlayer() << endl;
	out << "Course: " << scoreCard.getCourse().getName() << endl;
	out << "Tee color: " << getTeeColor(scoreCard.getTeeColor()) << endl;

	vector<GolfHole> holes = scoreCard.getCourse().getHoles();
	vector<size_t> resultsPerHole = scoreCard.getResultsPerHole();
	vector<size_t> pointsPerHole = scoreCard.getPointsPerHole();

	for (unsigned int i = 0; i < holes.size(); i++) {
		out << "Hole " << (i + 1) << ": " << holes[i].getPar() << "/"
				<< holes[i].getHcp() << "/" ;
		if (resultsPerHole[i] == 0) {
			out << "-";
		} else {
			out << resultsPerHole[i];
		}
		out << "/" << pointsPerHole[i] << endl;
	}

	out << "Total points: " << scoreCard.getTotalPoints();
	return out;
}

